package com.fadis.protocol;

import com.fadis.FadisException;
import com.fadis.FadisConstant;
import com.fadis.utils.SafeEncoder;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

/**
 * *3/r/n$3set\r\n$3key\r\n$5value  转成 RedisFrame
 */
public class CmdDecoder {

    public static CmdDecoder getDefault(){
        return new CmdDecoder();
    }

    public BaseRespProtocol decodeCmd(String cmd){
        return decodeCmd(ByteBuffer.wrap(SafeEncoder.encode(cmd)));
    }

    public BaseRespProtocol decodeCmd(ByteBuffer cmd){
        int position = cmd.position();
        byte start = cmd.get();
        cmd.position(position);
        if(start == '+'){
            SimpleString str = CmdDecoder.getDefault().decodeSimpleString(cmd);
            return str;
        }else if(start == '$'){
            BulkString str = CmdDecoder.getDefault().decodeBulkString(cmd);
            return str;
        }else if(start == '-'){
            ErrorString str = CmdDecoder.getDefault().decodeErrorString(cmd);
            return str;
        }else if(start == ':'){
            LongNumber n = CmdDecoder.getDefault().decodeLongNumber(cmd);
            return n;
        }else if(start == '*'){
            RespArray a = CmdDecoder.getDefault().decodeArray(cmd);
            return a;
        }else{
            throw new FadisException(4004, "非法的开头字符 " + (char)start + " at index " + cmd.position());
        }

    }

    private SimpleString decodeSimpleString(String cmd){
        // +OK/r/n
        String s = cmd.substring(1, cmd.length() - 2);
        return new SimpleString(s);
    }

    private SimpleString decodeSimpleString(ByteBuffer buf){
        // +OK/r/n
        byte start = buf.get();
        if(start != '+'){
            throw new FadisException(4010, "非简单字符串" + start);
        }

        List<Byte> bytes = new ArrayList<>();
        while(true){
            byte c = buf.get();
            if(c == FadisConstant.CR){
                break;
            }else{
                bytes.add(c);
            }
        }
        if(buf.get() != FadisConstant.LF){
            throw new FadisException(1003, "can not find LF");
        }

        byte[] bytes2 = new byte[bytes.size()];
        for (int i = 0; i < bytes.size(); i++) {
            bytes2[i] = bytes.get(i);
        }

        try {
            String s = new String(bytes2, "utf-8");
            return new SimpleString(s);
        } catch (UnsupportedEncodingException e) {
            throw new FadisException(4020, e.getMessage());
        }
    }

    private ErrorString decodeErrorString(String cmd){
        // -Error msg/r/n
        String s = cmd.substring(1, cmd.length() - 2);
        return new ErrorString(s);
    }

    private ErrorString decodeErrorString(ByteBuffer buf){
        // -Error msg/r/n
        byte start = buf.get();
        if(start != '-'){
            throw new FadisException(4010, "非错误字符串" + start);
        }

        List<Byte> bytes = new ArrayList<>();
        while(true){
            byte c = buf.get();
            if(c == FadisConstant.CR){
                break;
            }else{
                bytes.add(c);
            }
        }
        if(buf.get() != FadisConstant.LF){
            throw new FadisException(1003, "can not find LF");
        }

        byte[] bytes2 = new byte[bytes.size()];
        for (int i = 0; i < bytes.size(); i++) {
            bytes2[i] = bytes.get(i);
        }
        try {
            String s = new String(bytes2, "utf-8");
            return new ErrorString(s);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new FadisException(4020, e.getMessage());
        }
    }

    private LongNumber decodeLongNumber(String cmd){
        // :1000/r/n
        String s = cmd.substring(1, cmd.length() - 2);
        return new LongNumber(Long.parseLong(s));
    }

    private LongNumber decodeLongNumber(ByteBuffer buf){
        // :1000/r/n
        byte start = buf.get();
        if(start != ':'){
            throw new FadisException(4010, "非整数字符串" + start);
        }

        List<Byte> bytes = new ArrayList<>();
        while(true){
            byte c = buf.get();
            if(c == FadisConstant.CR){
                break;
            }else{
                bytes.add(c);
            }
        }
        if(buf.get() != FadisConstant.LF){
            throw new FadisException(1003, "can not find LF");
        }

        byte[] bytes2 = new byte[bytes.size()];
        for (int i = 0; i < bytes.size(); i++) {
            bytes2[i] = bytes.get(i);
        }
        try {
            String s = new String(bytes2, "utf-8");
            return new LongNumber(Long.parseLong(s));
        } catch (Exception e) {
            e.printStackTrace();
            throw new FadisException(4020, e.getMessage());
        }
    }

    private BulkString decodeBulkString(String cmd){
        // $6/r/nabcdef/r/n  字符串abcdef
        // $0\r\n\r\n  空串
        // $-1\r\n     null
        if(cmd.startsWith("$-1")){
            return new BulkString();
        }else if(cmd.startsWith("$0")){
            return new BulkString("");
        }else{
            int start = cmd.indexOf("\r\n", 0) + 2;
            int end = cmd.lastIndexOf("\r\n");
            String s = cmd.substring(start, end);
            return new BulkString(s);
        }
    }

    private BulkString decodeBulkString(ByteBuffer buf){
        // $6/r/nabcdef/r/n  字符串abcdef
        // $0\r\n\r\n  空串
        // $-1\r\n     null
        byte start = buf.get();
        if(start != '$'){
            throw new FadisException(4010, "非Bulk字符串" + start);
        }

        int len = parseRedisNumber(buf);

        byte[] bytes2 = new byte[len];
        buf.get(bytes2);
        if(buf.get() != FadisConstant.CR){
            throw new FadisException(1006, "can not find CR or CR");
        }
        if(buf.get() != FadisConstant.LF){
            throw new FadisException(1006, "can not find CR or LF");
        }
        try {
            String s = new String(bytes2, "utf-8");
            return new BulkString(s);
        } catch (Exception e) {
            e.printStackTrace();
            throw new FadisException(4020, e.getMessage());
        }
    }

    public RespArray decodeArray(String cmd){
        return decodeArray(ByteBuffer.wrap(SafeEncoder.encode(cmd)));
    }

    private RespArray decodeArray(ByteBuffer buf){
        RespArray array = new RespArray();
        byte start = buf.get();
        if(start != '*'){
            throw new FadisException(4010, "非数组" + start);
        }

        int len = parseRedisNumber(buf);
        if(len == 0) return array;
        if(len == -1) {
            array.makeNull();
            return array;
        }

        for (int i = 0; i < len; i++) {
            BaseRespProtocol cmd = decodeCmd(buf);
            array.add(cmd);
        }
        return array;
    }


    private static int parseRedisNumber(ByteBuffer byteBuf){
        byte readByte = byteBuf.get();
        boolean negative = readByte == '-';
        if(negative){
            readByte = byteBuf.get();
        }
        int result = 0;
        do{
//            System.out.println((char)readByte);
            int digit = readByte - '0';
            if (digit >= 0 && digit < 10) {
                result = (result * 10) + digit;
            }else{
                throw new FadisException(1002, "Invalid character in integer--" + (char)readByte);
            }
        }while ((readByte = byteBuf.get()) != FadisConstant.CR);

        if((readByte = byteBuf.get()) != FadisConstant.LF){
            throw new FadisException(1003, "can not find LF");
        }

        return negative ? -result : result;
    }

}
